﻿Imports System.ComponentModel
Imports FountainHead.ViewModel.Controls
Imports FountainHead.ViewModel
Namespace Controls
    Public Class BaseControl
        Inherits UserControl
        Implements INotifyPropertyChanged


        Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
#Region "Constructors"
        Public Sub New()
            'Initialization
            InitializeComponent()

        End Sub
        Public Sub New(ByVal vm As BaseControlViewModel)
            'Initialization
            InitializeComponent()
            ' Add any initialization after the InitializeComponent() call.
            snapOffsetX = 0
            snapOffsetY = 0

            viewModelValue = vm
            Left = vm.Left
            Top = vm.Top
            AddHandler LocationChanged, AddressOf LocationChangedHandler
            AddHandler ViewModel.PropertyChanged, AddressOf PropertyChangedHandler
        End Sub


#End Region

#Region "Property"

        Private viewModelValue As BaseControlViewModel
        Public Overridable ReadOnly Property ViewModel() As BaseControlViewModel
            Get
                Return viewModelValue
            End Get
        End Property

        Private snapPointValue As Point
        Public Property SnapPoint() As Point
            Get
                Return snapPointValue
            End Get
            Set(ByVal value As Point)
                snapPointValue = value
            End Set
        End Property

        Private ReadOnly snapsValue As New List(Of Terminal)
        Public ReadOnly Property Snaps As List(Of Terminal)
            Get
                Return snapsValue
            End Get
        End Property


#End Region

#Region "Event Handlers"
        Public Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(e.PropertyName))
        End Sub

        Private Sub LocationChangedHandler(ByVal sender As Object, ByVal e As EventArgs)
            ViewModel.Left = Left
            ViewModel.Top = Top
            SnapPoint = New Point(Left + snapOffsetX, Top + snapOffsetY)
        End Sub
        Private Sub PropertyChangedHandler(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
            'If Not ViewModel.Left = Left Then ViewModel.Left = Left
            'If Not ViewModel.Top = Top Then ViewModel.Top = Top
            Select Case e.PropertyName
                Case "Name"
                    Name = ViewModel.Name
            End Select
        End Sub
        Protected snapOffsetX As Integer
        Protected snapOffsetY As Integer

#End Region

#Region "Control Drag Methods"

        ' Event names to be used as keys for the parent Labels Events collection
        Private Const EVENT_STAYWITHINPARENTBOUNDSCHANGED As String = "StayWithinParentBoundsEvent"

        ' Holds the mouse position relative to the inside of our control when the mouse button goes down.
        Private m_CursorOffset As Point
        ' Used by the MoveMove event handler to show that the setup to move the control has completed.
        Private m_Moving As Boolean
        ' Used to store the current cursor shape when we start to move the control.
        Private m_CurrentCursor As Cursor
        ' Used to specify if our control should stay with the visible bounds of our parent container.
        Private m_StayWithinParent As Boolean




        <Bindable(True), DefaultValue(True), Category("Behavior"), _
    Description("Gets or sets whether the control will stay within its parent container's visible bounds."), _
    DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> _
        Public Property StayWithinParentBounds() As Boolean
            Get
                Return m_StayWithinParent
            End Get

            Set(ByVal value As Boolean)
                If value <> m_StayWithinParent Then
                    m_StayWithinParent = value
                    MoveControlWithinBounds()
                    Me.OnStayWithinParentBoundsChanged(EventArgs.Empty)
                End If
            End Set
        End Property

        Public Custom Event StayWithinParentBoundsChanged As EventHandler
            AddHandler(ByVal value As EventHandler)
                Events.AddHandler(EVENT_STAYWITHINPARENTBOUNDSCHANGED, value)
            End AddHandler

            RemoveHandler(ByVal value As EventHandler)
                MyBase.Events.RemoveHandler(EVENT_STAYWITHINPARENTBOUNDSCHANGED, value)
            End RemoveHandler

            RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
                CType(MyBase.Events(EVENT_STAYWITHINPARENTBOUNDSCHANGED), EventHandler).Invoke(sender, e)
            End RaiseEvent
        End Event

        Protected Overridable Sub OnStayWithinParentBoundsChanged(ByVal e As EventArgs)
            Dim newHandler As EventHandler = TryCast(MyBase.Events(EVENT_STAYWITHINPARENTBOUNDSCHANGED), EventHandler)
            If Not newHandler Is Nothing Then
                newHandler.Invoke(Me, e)
            End If
        End Sub

        Private Sub Control_HandleDestroyed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.HandleDestroyed
            Try
                RemoveHandler Me.Parent.Resize, AddressOf Parent_Resize
            Catch ex As NullReferenceException
            End Try
        End Sub

        Private Sub Control_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
            If StayWithinParentBounds Then
                MoveControlWithinBounds()
            End If
        End Sub

        Private Sub Control_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
            If e.Button = Windows.Forms.MouseButtons.Right Then
                m_CursorOffset = e.Location
                m_CurrentCursor = MyBase.Cursor
                MyBase.Cursor = Cursors.SizeAll
                m_Moving = True
            End If
        End Sub

        Private Sub Control_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
            If m_Moving Then
                Dim clientPosition As Point = MyBase.Parent.PointToClient(System.Windows.Forms.Cursor.Position)
                Dim adjustedLocation As New Point(clientPosition.X - m_CursorOffset.X, clientPosition.Y - m_CursorOffset.Y)

                If StayWithinParentBounds Then
                    MoveControlWithinBounds(adjustedLocation)
                Else
                    MyBase.Location = adjustedLocation
                End If
            End If
        End Sub

        Private Sub Control_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseUp
            m_Moving = False
            MyBase.Cursor = m_CurrentCursor
        End Sub

        Private Sub Control_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ParentChanged
            Try
                AddHandler Me.Parent.Resize, AddressOf Parent_Resize
            Catch ex As NullReferenceException
            End Try
        End Sub

        Private Sub Parent_Resize(ByVal sender As Object, ByVal e As System.EventArgs)
            If StayWithinParentBounds Then
                MoveControlWithinBounds()
            End If
        End Sub

        Private Overloads Sub MoveControlWithinBounds()
            If MyBase.IsHandleCreated Then
                With MyBase.Location
                    MoveControlWithinBounds(New Point(.X, .Y))
                End With
            End If
        End Sub

        Private Overloads Sub MoveControlWithinBounds(ByVal location As Point)
            If MyBase.IsHandleCreated Then
                Dim x As Integer = location.X
                Dim y As Integer = location.Y

                With MyBase.Parent.ClientRectangle
                    If x > .Right - MyBase.Width Then
                        x = .Right - MyBase.Width
                    End If

                    If y > .Bottom - MyBase.Height Then
                        y = .Bottom - MyBase.Height
                    End If

                    If x < .Left Then
                        x = .Left
                    End If

                    If y < .Top Then
                        y = .Top
                    End If
                End With
                MyBase.Location = New Point(x, y)
            End If
        End Sub
#End Region

#Region "Methods"
        Protected Sub AddTerminal(ByVal pt As Terminal)
            snapsValue.Add(pt)
        End Sub

        Protected Sub ClearTerminal()
            snapsValue.Clear()
        End Sub

        Public Function NearestTerminal(ByVal pt As Point) As Terminal
            Dim dist As Double = 1000
            Dim result As New Terminal ' = New Point(0, 0)
            For Each p In Snaps
                Dim d = Math.Sqrt((Math.Abs(p.Point.X - pt.X) ^ 2) + _
                   (Math.Abs(p.Point.Y - pt.Y) ^ 2))
                If d < dist Then
                    result = p
                    dist = d
                End If
            Next
            Return result
        End Function

        Private Sub InitializeComponent()
            Me.SuspendLayout()
            '
            'BaseControl
            '
            Me.Name = "BaseControl"
            Me.ResumeLayout(False)

        End Sub
#End Region
    End Class
End Namespace